#include "stdafx.h"
#include "SqliteAdapter.h"
#include "SqliteInitInfo.h"
#include "SqliteTypeUtil.h"
#include "RecordException.h"
#include "sqlite3.h"
#include <vector>
#include <sstream>

#ifdef WIN32
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#endif

namespace serika
{
	namespace db
	{
		SqliteAdapter::SqliteAdapter()
			: mSqlite(0)
		{
		}

		SqliteAdapter::~SqliteAdapter()
		{
			//this->Destroy();
		}

		bool SqliteAdapter::Create(const Any& initInfo)
		{
			const SqliteInitInfo& info = initInfo.Get<SqliteInitInfo>();

			int ret = sqlite3_open(info.dbFile.c_str(), &mSqlite);
			if (ret != SQLITE_OK)
			{
				return false;
			}

			return true;
		}

		void SqliteAdapter::Destroy()
		{
			if (mSqlite)
			{
				sqlite3_close(mSqlite);
				mSqlite = 0;
			}
			delete this;
		}

		int SqliteAdapter::ReadRecords(std::vector<FieldMap*>& resultSet, 
			const std::string& tableName,
			const std::string& conditions, 
			int offset,
			int limit,
			const std::string& select, 
			const std::string& joins, 
			const std::string& order, 
			const std::string& group,
			const std::string& having)
		{
			resultSet.clear();

			std::string statement = this->ConstructSelect(tableName, conditions, offset, limit, select, joins, order, group, having);

			sqlite3_stmt* stmt = this->Prepare(statement);

			int ret = 0;
			do
			{
				ret = sqlite3_step(stmt);
				switch (ret)
				{
				case SQLITE_ROW:
					{
						FieldMap* fm = new FieldMap;
						SqliteTypeUtil::InsertValue(stmt, fm);
						resultSet.push_back(fm);
					}
					break;

				case SQLITE_DONE:
					break;

				default:
					this->Finalize(stmt);
					this->ThrowRecordException(ret);
				}
			}while (ret == SQLITE_ROW);

			this->Finalize(stmt);

			return resultSet.size();
		}

		int SqliteAdapter::Count(const std::string& tableName, const std::string& column, const std::string& conditions, const std::string& joins)
		{
			std::string select;
			if (column == "*")
			{
				select = "COUNT(*) AS `count_all`";
			}
			else
			{
				std::stringstream ss;
				ss << "COUNT(`" << column << "`) AS `count_all`";
				select = ss.str();
			}
			std::string statement = this->ConstructSelect(tableName, conditions, 0, 0, select, joins, "", "", "");

			sqlite3_stmt* stmt = this->Prepare(statement);
			int ret = sqlite3_step(stmt);

			int rows = SQLITE_ROW == ret ? sqlite3_column_int(stmt, 0) : 0;
			
			this->Finalize(stmt);

			return rows;
		}

		bool SqliteAdapter::CreateRecord(FieldMap* fields, const std::string& tableName, const std::string& primaryKey)
		{
			std::string statement = this->ConstructInsert(fields, tableName);
			this->Exec(statement);

			std::stringstream ss;
			ss << "SELECT * FROM `" << tableName << "` WHERE `" << primaryKey << "`='" << sqlite3_last_insert_rowid(mSqlite) << "'";
			statement = ss.str();
			sqlite3_stmt* stmt = this->Prepare(statement.c_str());
			int ret = sqlite3_step(stmt);
			if (SQLITE_ROW != ret)
			{
				this->Finalize(stmt);
				this->ThrowRecordException(ret);
			}
			SqliteTypeUtil::InsertValue(stmt, fields);

			this->Finalize(stmt);
			return true;
		}

		bool SqliteAdapter::CreateRecords(std::vector<FieldMap*>& fieldMaps, const std::string& tableName, const std::string& primaryKey)
		{
			std::string statement = this->ConstructMultipleInsert(fieldMaps, tableName);

			std::vector<sqlite3_int64> insertIds;
			const char* sql = statement.c_str();
			int len = ::strlen(sql);
			do
			{
				sqlite3_stmt* stmt = 0;
				const char* remain = 0;
				int ret = sqlite3_prepare_v2(mSqlite, sql, len, &stmt, &remain);
				if (SQLITE_OK != ret || stmt == 0)
				{
					this->ThrowRecordException(ret);
				}
				ret = sqlite3_step(stmt);
				if (SQLITE_DONE != ret)
				{
					this->Finalize(stmt);
					this->ThrowRecordException(ret);
				}
				insertIds.push_back(sqlite3_last_insert_rowid(mSqlite));
				this->Finalize(stmt);
				sql = remain;
				len = ::strlen(sql);
			}while(len > 0);

			std::stringstream ss;
			ss << "SELECT * FROM `" << tableName << "` WHERE `" << primaryKey << "`in (";
			for (std::vector<sqlite3_int64>::iterator iter = insertIds.begin();
				iter != insertIds.end(); ++iter)
			{
				if (insertIds.begin() != iter)
				{
					ss << ",";
				}
				ss << *iter;
			}
			ss << ")";

			statement = ss.str();
			sqlite3_stmt* stmt = this->Prepare(statement);

			std::vector<FieldMap*>::iterator iter = fieldMaps.begin();
			int ret = 0;
			do
			{
				ret = sqlite3_step(stmt);
				switch (ret)
				{
				case SQLITE_ROW:
					{
						FieldMap* fm = *iter;
						SqliteTypeUtil::InsertValue(stmt, fm);
					}
					break;

				case SQLITE_DONE:
					break;

				default:
					this->Finalize(stmt);
					this->ThrowRecordException(ret);
				}
				++iter;
			}while (ret == SQLITE_ROW);

			this->Finalize(stmt);
			return true;
		}

		int SqliteAdapter::UpdateAllRecords(const std::string& settings, const std::string& conditions, const std::string& tableName)
		{
			std::string statement = this->ConstructUpdate(tableName, settings, conditions);
			this->Exec(statement);
			return sqlite3_changes(mSqlite);
		}

		int SqliteAdapter::DeleteRecord(const std::string& conditions, const std::string& tableName)
		{
			std::string statement = this->ConstructDelete(tableName, conditions);
			this->Exec(statement);
			return sqlite3_changes(mSqlite);
		}

		void SqliteAdapter::BeginTransaction()
		{
			this->Exec("begin");
		}

		void SqliteAdapter::CommitTransaction()
		{
			this->Exec("commit");
		}

		void SqliteAdapter::RollbackTransaction()
		{
			this->Exec("rollback");
		}

		std::string SqliteAdapter::ConstructMultipleInsert(const std::vector<FieldMap*>& fieldMaps, const std::string& tableName)
		{
			std::stringstream ss, prefix, f, v;
			prefix << "INSERT INTO `" << tableName << "` ";

			const FieldMap* fields = fieldMaps.back();
			f << "(";
			for (FieldMap::const_iterator itr = fields->begin(); itr != fields->end(); ++itr)
			{
				if (itr != fields->begin())
				{
					f << ",";
				}
				f << "`" << itr->first << "`";
			}
			f << ")";

			for (std::vector<FieldMap*>::const_iterator itr = fieldMaps.begin(); itr != fieldMaps.end(); ++itr)
			{
				ss << prefix.str() << f.str() << " VALUES ";
				v << "(";
				fields = *itr;
				for (FieldMap::const_iterator itr2 = fields->begin(); itr2 != fields->end(); ++itr2)
				{
					if (itr2 != fields->begin())
					{
						v << ",";
					}
					if (itr2->second.type() == typeid(std::string))
					{
						v << "'" << this->EscapeString(itr2->second.Get<std::string>()) << "'";
					}
					else 
					{
						v << "'" << itr2->second << "'";
					}
				}
				v << ");";
			}

			return ss.str();
		}

		inline void SqliteAdapter::ThrowRecordException(int code)
		{
			const char* msg = sqlite3_errmsg(mSqlite);
			this->ThrowRecordException(code, msg);
		}

		inline void SqliteAdapter::ThrowRecordException(int code, const char* message)
		{
			ut::Sstream ss;
			ss << "Sqlite Error: " << message;
			throw RecordException(code, ss.str().c_str());
		}

		inline sqlite3_stmt* SqliteAdapter::Prepare(const std::string& sql)
		{
			sqlite3_stmt* stmt = 0;
			int ret = sqlite3_prepare_v2(mSqlite, sql.c_str(), sql.size() + 1, &stmt, 0);
			if (SQLITE_OK != ret || stmt == 0)
			{
				this->ThrowRecordException(ret);
			}
			return stmt;
		}

		inline void SqliteAdapter::Finalize(sqlite3_stmt* stmt)
		{
			int ret = sqlite3_finalize(stmt);
			if (SQLITE_OK != ret)
			{
				this->ThrowRecordException(ret);
			}
		}

		inline void SqliteAdapter::Exec(const std::string& sql)
		{
			char* errMsg = 0;
			int ret = sqlite3_exec(mSqlite, sql.c_str(), 0, 0, &errMsg);
			if (SQLITE_OK != ret)
			{
				this->ThrowRecordException(ret, errMsg);
			}
		}
	}
}
